// // ******************************************************************
// //       /\ /|       @file       SubtitleConversionHelper.cs
// //       \ V/        @brief      await29
// //       | "")       @author     Await, 285283010@qq.com
// //       /  |
// //      /  \\        @Modified   2025-02-21 20:02
// //    *(__\_\        @Copyright  Copyright (c) , 2025-02-21 20:02 Await
// // ******************************************************************

using System.Runtime.InteropServices;
using Xabe.FFmpeg;
using Tesseract;
using System.Text.RegularExpressions;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using System.Linq;
using System.Threading;

namespace AutomaticSubtitles.Service.Infrastructure.Helper;

/// <summary>
/// 字幕转换进度事件参数
/// </summary>
public class ConversionProgressEventArgs : EventArgs
{
    public int    TotalFrames        { get; set; }
    public int    CurrentFrame       { get; set; }
    public string Status             { get; set; }
    public double ProgressPercentage => (double)CurrentFrame / TotalFrames * 100;
}

/// <summary>
/// 字幕转换帮助类
/// </summary>
public static class SubtitleConversionHelper
{
    /// <summary>
    /// 支持的字幕格式
    /// </summary>
    public static readonly string[] SupportedFormats =
    {
        "pgs", // 蓝光图形字幕
        "sup", // DVD图形字幕
        "idx", // VobSub索引文件
        "sub", // VobSub字幕数据
        "ass", // Advanced SubStation Alpha
        "ssa", // SubStation Alpha
        "srt"  // SubRip Text
    };

    /// <summary>
    /// 进度报告事件
    /// </summary>
    public static event EventHandler<ConversionProgressEventArgs>? ConversionProgress;

    /// <summary>
    /// 将字幕转换为SRT格式
    /// </summary>
    public static async Task<string> ConvertToSrtAsync(ISubtitleStream subItem, string outputFilePath, CancellationToken cancellationToken = default)
    {
        var extension = Path.GetExtension(subItem.Path)?.TrimStart('.').ToLower();
        if (string.IsNullOrEmpty(extension) || !SupportedFormats.Contains(extension)) throw new NotSupportedException($"不支持的字幕格式: {extension}");

        return extension switch
        {
            "pgs"          => await ConvertPgsToSrtAsync(subItem, outputFilePath, cancellationToken),
            "sup"          => await ConvertPgsToSrtAsync(subItem, outputFilePath, cancellationToken), // 使用相同的图形字幕处理方法
            "idx" or "sub" => await ConvertVobSubToSrtAsync(subItem, outputFilePath, cancellationToken),
            "ass" or "ssa" => await ConvertAssToSrtAsync(subItem, outputFilePath, cancellationToken),
            _              => throw new NotSupportedException($"暂不支持的字幕格式: {extension}")
        };
    }

    /// <summary>
    /// 将PGS字幕转换为SRT格式
    /// </summary>
    /// <param name="subItem">字幕流</param>
    /// <param name="outputFilePath">输出文件路径</param>
    /// <returns>转换后的SRT文件路径</returns>
    public static async Task<string> ConvertPgsToSrtAsync(ISubtitleStream subItem, string outputFilePath, CancellationToken cancellationToken = default)
    {
        try
        {
            // 确保FFmpeg已初始化
            await InitializeFfmpegAsync(cancellationToken);

            // 构建输出文件路径
            var srtPath = $"{outputFilePath}.{subItem.Language}.{subItem.Index}.srt";
            var tempImagePath = Path.Combine(Path.GetDirectoryName(srtPath)!, "temp_ocr");

            try
            {
                // 创建临时目录
                Directory.CreateDirectory(tempImagePath);

                // 使用FFmpeg提取字幕帧为图片
                await ExtractSubtitleFramesAsync(subItem, tempImagePath, cancellationToken);

                // 使用Tesseract进行OCR识别
                await ProcessOcrWithTimingAsync(tempImagePath, srtPath, subItem.Language, cancellationToken);

                return srtPath;
            }
            finally
            {
                // 清理临时文件
                if (Directory.Exists(tempImagePath)) Directory.Delete(tempImagePath, true);
            }
        }
        catch (OperationCanceledException)
        {
            throw;
        }
        catch (Exception ex)
        {
            throw new Exception($"字幕转换失败: {ex.Message}", ex);
        }
    }

    /// <summary>
    /// 将VobSub字幕转换为SRT格式
    /// </summary>
    private static async Task<string> ConvertVobSubToSrtAsync(ISubtitleStream subItem, string outputFilePath, CancellationToken cancellationToken = default)
    {
        try
        {
            // 确保FFmpeg已初始化
            await InitializeFfmpegAsync(cancellationToken);

            // 构建输出文件路径
            var srtPath = $"{outputFilePath}.{subItem.Language}.{subItem.Index}.srt";
            var tempImagePath = Path.Combine(Path.GetDirectoryName(srtPath)!, "temp_vobsub");

            try
            {
                // 创建临时目录
                Directory.CreateDirectory(tempImagePath);

                // 首先将VobSub转换为PGS格式（中间格式）
                var tempPgsPath = Path.Combine(tempImagePath, "temp.pgs");
                await ConvertVobSubToPgsAsync(subItem, tempPgsPath, cancellationToken);

                // 使用优化后的OCR处理
                await ProcessOcrWithTimingAsync(tempImagePath, srtPath, subItem.Language, cancellationToken);

                return srtPath;
            }
            finally
            {
                // 清理临时文件
                if (Directory.Exists(tempImagePath)) Directory.Delete(tempImagePath, true);
            }
        }
        catch (OperationCanceledException)
        {
            throw;
        }
        catch (Exception ex)
        {
            throw new Exception($"VobSub字幕转换失败: {ex.Message}", ex);
        }
    }

    /// <summary>
    /// 将VobSub转换为PGS格式
    /// </summary>
    private static async Task ConvertVobSubToPgsAsync(ISubtitleStream subItem, string outputPath, CancellationToken cancellationToken = default)
    {
        try
        {
            var conversion = FFmpeg.Conversions.New()
                .AddStream(subItem)
                .SetOutputFormat("pgs")
                .SetOutput(outputPath);

            conversion.OnProgress += (sender, args) =>
            {
                cancellationToken.ThrowIfCancellationRequested();
                ConversionProgress?.Invoke(null, new ConversionProgressEventArgs
                {
                    TotalFrames = 100,
                    CurrentFrame = (int)args.Percent,
                    Status = $"正在转换VobSub到PGS: {args.Percent}%"
                });
            };

            await conversion.Start(cancellationToken);
        }
        catch (OperationCanceledException)
        {
            throw;
        }
        catch (Exception ex)
        {
            throw new Exception($"VobSub到PGS转换失败: {ex.Message}", ex);
        }
    }

    /// <summary>
    /// 使用时间信息处理OCR识别
    /// </summary>
    private static async Task ProcessOcrWithTimingAsync(string imagePath, string outputSrtPath, string language, CancellationToken cancellationToken = default)
    {
        try
        {
            // 使用迭代器模式获取文件列表，避免一次性加载所有文件到内存
            var imageFiles = GetImageFiles(imagePath).ToList();
            var totalFiles = imageFiles.Count;

            // 创建并行处理选项
            var parallelOptions = new ParallelOptions
            {
                MaxDegreeOfParallelism = Environment.ProcessorCount,
                CancellationToken = cancellationToken
            };

            // 使用分块处理，避免一次性处理太多数据
            const int batchSize = 50;
            var processedCount = 0;
            var ocrResults = new ConcurrentDictionary<int, string>();

            // 初始化Tesseract引擎池，限制最大引擎数量
            var maxEngines = Math.Min(Environment.ProcessorCount, 4); // 最多4个引擎
            var enginePool = new ConcurrentBag<TesseractEngine>();
            for (var i = 0; i < maxEngines; i++)
                enginePool.Add(new TesseractEngine(
                    Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "tessdata"),
                    MapLanguageCode(language),
                    EngineMode.Default));

            try
            {
                // 分批处理文件
                for (var i = 0; i < totalFiles; i += batchSize)
                {
                    var batch = imageFiles.Skip(i).Take(batchSize).ToList();

                    // 并行处理当前批次
                    await Parallel.ForEachAsync(batch, parallelOptions, async (imageFile, ct) =>
                    {
                        ct.ThrowIfCancellationRequested();

                        TesseractEngine? engine = null;
                        try
                        {
                            // 从池中获取引擎，如果无法获取则等待
                            while (!enginePool.TryTake(out engine)) await Task.Delay(100, ct);

                            // 配置Tesseract
                            engine.SetVariable("tessedit_char_whitelist", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,!?-:;'\"()[]{}");
                            engine.SetVariable("tessedit_pageseg_mode", "6");

                            string text;
                            // 使用 using 块确保及时释放资源
                            using (var img = Pix.LoadFromFile(imageFile.Path))
                            using (var page = engine.Process(img))
                            {
                                text = page.GetText().Trim();
                            }

                            // 清理和优化文本
                            text = await Task.Run(() => CleanOcrText(text), ct);

                            // 存储结果
                            if (!string.IsNullOrWhiteSpace(text)) ocrResults.TryAdd(imageFile.FrameNumber, text);

                            Interlocked.Increment(ref processedCount);

                            // 报告进度
                            ConversionProgress?.Invoke(null, new ConversionProgressEventArgs
                            {
                                TotalFrames = totalFiles,
                                CurrentFrame = processedCount,
                                Status = $"正在进行OCR识别: {processedCount}/{totalFiles}"
                            });
                        }
                        finally
                        {
                            // 将引擎放回池中
                            if (engine != null) enginePool.Add(engine);
                        }
                    });

                    // 处理完一批后，立即写入结果并释放内存
                    if (i > 0 && i % (batchSize * 2) == 0)
                    {
                        await WriteProcessedResultsAsync(ocrResults, outputSrtPath, i - batchSize * 2, i, cancellationToken);

                        // 清理已写入的结果
                        var keysToRemove = ocrResults.Keys.Where(k => k < i).ToList();
                        foreach (var key in keysToRemove) ocrResults.TryRemove(key, out _);
                    }

                    // 强制垃圾回收
                    if (i > 0 && i % (batchSize * 4) == 0) GC.Collect();
                }

                // 写入最后的结果
                await WriteProcessedResultsAsync(ocrResults, outputSrtPath, 0, totalFiles, cancellationToken);
            }
            finally
            {
                // 清理资源
                foreach (var engine in enginePool) engine.Dispose();
            }
        }
        catch (OperationCanceledException)
        {
            throw;
        }
        catch (Exception ex)
        {
            throw new Exception($"OCR处理失败: {ex.Message}", ex);
        }
    }

    /// <summary>
    /// 使用迭代器模式获取图片文件列表
    /// </summary>
    private static IEnumerable<(string Path, int FrameNumber)> GetImageFiles(string imagePath)
    {
        foreach (var file in Directory.EnumerateFiles(imagePath, "frame_*.png"))
        {
            var frameNumber = int.Parse(Path.GetFileNameWithoutExtension(file).Split('_')[1]);
            yield return (file, frameNumber);
        }
    }

    /// <summary>
    /// 写入处理结果到SRT文件
    /// </summary>
    private static async Task WriteProcessedResultsAsync(
        ConcurrentDictionary<int, string> results,
        string outputSrtPath,
        int startFrame,
        int endFrame,
        CancellationToken cancellationToken)
    {
        var writer = new StreamWriter(outputSrtPath, true, System.Text.Encoding.UTF8);
        try
        {
            var subtitleIndex = startFrame / 3 + 1; // 假设每3帧一个字幕
            var currentText = "";
            var currentStartFrame = startFrame;

            for (var frame = startFrame; frame < endFrame; frame++)
            {
                cancellationToken.ThrowIfCancellationRequested();

                if (results.TryGetValue(frame, out var text))
                {
                    if (string.IsNullOrEmpty(currentText))
                    {
                        currentStartFrame = frame;
                        currentText = text;
                    }
                    else if (text != currentText)
                    {
                        await WriteSubtitleEntryAsync(writer, subtitleIndex++, currentStartFrame, frame - 1, currentText, cancellationToken);
                        currentStartFrame = frame;
                        currentText = text;
                    }
                }
                else if (!string.IsNullOrEmpty(currentText))
                {
                    await WriteSubtitleEntryAsync(writer, subtitleIndex++, currentStartFrame, frame - 1, currentText, cancellationToken);
                    currentText = "";
                }
            }

            // 处理最后一个字幕
            if (!string.IsNullOrEmpty(currentText)) await WriteSubtitleEntryAsync(writer, subtitleIndex, currentStartFrame, endFrame - 1, currentText, cancellationToken);
        }
        finally
        {
            await writer.DisposeAsync();
        }
    }

    /// <summary>
    /// 写入字幕条目
    /// </summary>
    private static async Task WriteSubtitleEntryAsync(StreamWriter writer, int index, int startFrame, int endFrame, string text, CancellationToken cancellationToken = default)
    {
        cancellationToken.ThrowIfCancellationRequested();

        // 假设帧率为23.976fps
        const double frameRate = 23.976;
        var startTime = TimeSpan.FromSeconds(startFrame / frameRate);
        var endTime = TimeSpan.FromSeconds(endFrame / frameRate);

        await writer.WriteLineAsync(index.ToString());
        await writer.WriteLineAsync($"{FormatTimestamp(startTime)} --> {FormatTimestamp(endTime)}");
        await writer.WriteLineAsync(text);
        await writer.WriteLineAsync();
    }

    /// <summary>
    /// 格式化时间戳
    /// </summary>
    private static string FormatTimestamp(TimeSpan time)
    {
        return $"{time:hh\\:mm\\:ss\\,fff}";
    }

    /// <summary>
    /// 清理OCR文本
    /// </summary>
    private static string CleanOcrText(string text)
    {
        if (string.IsNullOrWhiteSpace(text))
            return string.Empty;

        // 移除多余的空白字符
        text = Regex.Replace(text, @"\s+", " ").Trim();

        // 修复常见的OCR错误
        text = text.Replace("l", "I") // 小写l通常被误识别为大写I
            .Replace("0", "O")        // 数字0通常被误识别为字母O
            .Replace("|", "I");       // 竖线通常被误识别为大写I

        // 移除无效的标点符号组合
        text = Regex.Replace(text, @"[.]{2,}", "...");
        text = Regex.Replace(text, @"[\?!]{2,}", "?!");

        return text;
    }

    /// <summary>
    /// 将ASS/SSA字幕转换为SRT格式
    /// </summary>
    private static async Task<string> ConvertAssToSrtAsync(ISubtitleStream subItem, string outputFilePath, CancellationToken cancellationToken = default)
    {
        var srtPath = $"{outputFilePath}.{subItem.Language}.{subItem.Index}.srt";

        try
        {
            // 使用FFmpeg直接转换ASS/SSA到SRT
            var conversion = FFmpeg.Conversions.New()
                .AddStream(subItem)
                .SetOutputFormat("srt")
                .SetOutput(srtPath);

            // 添加进度报告
            conversion.OnProgress += (sender, args) =>
            {
                cancellationToken.ThrowIfCancellationRequested();
                ConversionProgress?.Invoke(null, new ConversionProgressEventArgs
                {
                    TotalFrames = 100,
                    CurrentFrame = (int)args.Percent,
                    Status = $"正在转换ASS/SSA字幕: {args.Percent}%"
                });
            };

            await conversion.Start(cancellationToken);
            return srtPath;
        }
        catch (Exception ex)
        {
            throw new Exception($"ASS/SSA字幕转换失败: {ex.Message}", ex);
        }
    }

    /// <summary>
    /// 初始化FFmpeg
    /// </summary>
    private static async Task InitializeFfmpegAsync(CancellationToken cancellationToken = default)
    {
        try
        {
            // 根据操作系统选择FFmpeg路径
            string ffmpegPath;
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                ffmpegPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ffmpeg", "win-x64");
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
                ffmpegPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ffmpeg", "linux-x64");
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
                ffmpegPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ffmpeg", "osx-x64");
            else
                throw new PlatformNotSupportedException("不支持的操作系统平台");

            // 设置FFmpeg可执行文件路径
            FFmpeg.SetExecutablesPath(ffmpegPath);
        }
        catch (Exception ex)
        {
            throw new Exception($"FFmpeg初始化失败: {ex.Message}", ex);
        }
    }

    /// <summary>
    /// 提取字幕帧为图片
    /// </summary>
    private static async Task ExtractSubtitleFramesAsync(ISubtitleStream subItem, string outputPath, CancellationToken cancellationToken = default)
    {
        try
        {
            // 创建分片目录
            var chunkSize = 100; // 每个分片处理100帧
            var chunks = new List<string>();
            for (var i = 0; i < 10; i++) // 假设最多1000帧，创建10个分片
            {
                var chunkPath = Path.Combine(outputPath, $"chunk_{i}");
                Directory.CreateDirectory(chunkPath);
                chunks.Add(chunkPath);
            }

            // 并行处理选项
            var parallelOptions = new ParallelOptions
            {
                MaxDegreeOfParallelism = Environment.ProcessorCount
            };

            // 并行提取字幕帧
            var tasks = chunks.Select(async (chunkPath, index) =>
            {
                var startFrame = index * chunkSize + 1;
                var conversion = FFmpeg.Conversions.New()
                    .AddStream(subItem)
                    .AddParameter($"-start_number {startFrame}")
                    .AddParameter($"-vframes {chunkSize}")
                    .SetOutputFormat("image2")
                    .SetOutput(Path.Combine(chunkPath, $"frame_%d.png"));

                // 添加进度报告
                conversion.OnProgress += (sender, args) =>
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    ConversionProgress?.Invoke(null, new ConversionProgressEventArgs
                    {
                        TotalFrames = chunks.Count * 100,
                        CurrentFrame = index * 100 + (int)args.Percent,
                        Status = $"正在提取字幕帧 分片 {index + 1}/{chunks.Count}: {args.Percent}%"
                    });
                };

                await conversion.Start(cancellationToken);

                // 移动文件到主输出目录
                foreach (var file in Directory.GetFiles(chunkPath, "frame_*.png"))
                {
                    var fileName = Path.GetFileName(file);
                    var targetPath = Path.Combine(outputPath, fileName);
                    if (File.Exists(targetPath))
                        File.Delete(targetPath);
                    File.Move(file, targetPath);
                }
            }).ToList();

            // 等待所有任务完成
            await Task.WhenAll(tasks);

            // 清理分片目录
            foreach (var chunk in chunks)
                if (Directory.Exists(chunk))
                    Directory.Delete(chunk, true);
        }
        catch (Exception ex)
        {
            throw new Exception($"字幕帧提取失败: {ex.Message}", ex);
        }
    }

    /// <summary>
    /// 映射语言代码到Tesseract支持的格式
    /// </summary>
    private static string MapLanguageCode(string language)
    {
        // 简化的语言代码映射
        return language.ToLower() switch
        {
            "zh" or "chi" => "chi_sim", // 简体中文
            "zht"         => "chi_tra", // 繁体中文
            "en" or "eng" => "eng",     // 英语
            "jpn" or "ja" => "jpn",     // 日语
            "kor" or "ko" => "kor",     // 韩语
            _             => "eng"      // 默认使用英语
        };
    }
}